/**
 * 
 */
package name.webdizz.gadget.four.envelope.client.request;

import name.webdizz.gadget.four.envelope.client.domain.Envelope;
import name.webdizz.gadget.four.envelope.client.domain.Expense;
import name.webdizz.gadget.four.envelope.client.domain.ExpenseItem;
import name.webdizz.gadget.four.envelope.client.domain.Goal;
import name.webdizz.gadget.four.envelope.client.domain.User;
import name.webdizz.gadget.four.envelope.client.util.Dates;
import name.webdizz.gadget.four.envelope.client.util.EnvelopeProvider;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.URL;
import com.google.gwt.http.client.RequestBuilder.Method;

/**
 * The HTTP requests invoker. Encapsulates logic to call JS functions from Java.
 * 
 * @author Izzet_Mustafayev
 * 
 */
public class RequestInvoker {

	/**
	 * Holds header attributes to pass to remote service for authentication.
	 */
	private HeaderAttributes attributes;

	private static final String BASE_URL = "https://www.4konverta.com/data/";
	private static final String USER_URL = BASE_URL + "{user}";
	private static final String ENVELOPE_URL = BASE_URL
			+ "{user}/execution/{envelopeBegin}";
	private static final String CREDIT_GOAL_URL = BASE_URL
			+ "{user}/actualGoalCredit/{goalId}/{plannedDate}";
	private static final String CREDIT_EXPENSE_URL = BASE_URL
			+ "{user}/actualExpense/{expenseId}/{plannedDate}";
	private static final String DAILY_EXPENSE_URL = BASE_URL
			+ "{user}/dailyExpense/{personId}/{date}";

	public RequestInvoker(final HeaderAttributes attributes) {
		this.attributes = attributes;
		// init tracker
		RequestInvokerJsni.initTracker();
	}

	/**
	 * Load {@link Envelope} data.
	 * 
	 * @param callback
	 *            the {@link RequestCallback} to invoke after loading succeed
	 */
	public void loadEnvelope(final RequestCallback callback) {
		String url = ENVELOPE_URL.replace("{user}", EnvelopeProvider.instance()
				.getEnvelope().getUser().getUsername());
		url = url.replace("{envelopeBegin}", Dates.getWeekBeginDate());
		execute(url, RequestBuilder.GET, callback);
	}

	/**
	 * Loads {@link User} data.
	 * 
	 * @param callback
	 *            the {@link RequestCallback} to invoke after loading succeed
	 */
	public void loadUser(final RequestCallback callback) {
		String url = USER_URL.replace("{user}", EnvelopeProvider.instance()
				.getEnvelope().getUser().getUsername());
		execute(url, RequestBuilder.GET, callback);
	}

	/**
	 * Perform storing of expense.
	 * 
	 * @param expense
	 *            the URL to perform storing
	 * @param callback
	 *            the {@link RequestCallback} to invoke after operation proceed
	 */
	public void saveExpense(final Expense expense,
			final RequestCallback callback) {
		String url = DAILY_EXPENSE_URL
				+ "/?expression={expression}&account={account}&currency={currency}";
		url = url.replace("{user}", EnvelopeProvider.instance().getEnvelope()
				.getUser().getUsername());
		url = url.replace("{personId}", expense.getPersonId());
		url = url.replace("{date}", expense.getDate());
		url = url.replace("{account}", expense.getDefaultAccount());
		url = url.replace("{currency}", expense.getCurrency().getIdentity());
		url = url.replace("{expression}", URL.encode(expense.getExpense()));
		execute(url, RequestBuilder.POST, callback);
	}

	/**
	 * Perform credit goal.
	 * 
	 * @param goal
	 *            the {@link Goal} to credit
	 * @param callback
	 *            the {@link RequestCallback} to invoke after operation succeed
	 */
	public void creditGoal(final Goal goal, final RequestCallback callback) {
		String url = CREDIT_GOAL_URL.replace("{user}", EnvelopeProvider
				.instance().getEnvelope().getUser().getUsername());
		url = url.replace("{goalId}", goal.getIdentity().toString());
		url = url.replace("{plannedDate}", goal.getDate());
		url += "?value={actual}&account={account}&goalAccount={goalAccount}&currency={currency}";
		url = url.replace("{actual}", goal.getActual().toString());
		url = url.replace("{goalAccount}", goal.getAccount());
		url = url.replace("{account}", goal.getAccount());
		url = url.replace("{currency}", goal.getCurrency().getIdentity());
		execute(url, RequestBuilder.POST, callback);
	}

	/**
	 * Perform credit expense.
	 * 
	 * @param expense
	 *            the {@link ExpenseItem} to credit
	 * @param callback
	 *            the {@link RequestCallback} to invoke after operation succeed
	 */
	public void creditExpense(final ExpenseItem expense,
			final RequestCallback callback) {
		String url = CREDIT_EXPENSE_URL.replace("{user}", EnvelopeProvider
				.instance().getEnvelope().getUser().getUsername());
		url = url.replace("{expenseId}", expense.getIdentity().toString());
		url = url.replace("{plannedDate}", expense.getDate());
		url += "?value={actual}&account={account}&currency={currency}";
		url = url.replace("{actual}", expense.getActual().toString());
		url = url.replace("{account}", expense.getDefaultAccount());
		url = url.replace("{currency}", expense.getCurrency().getIdentity());
		execute(url, RequestBuilder.POST, callback);
	}

	/**
	 * Track gadget usage to Google Analytics.
	 * 
	 * @param action
	 *            the action to track
	 * @param username
	 *            the user performed action
	 */
	public void trackUsage(final String action, final String username) {
		RequestInvokerJsni.trackUsage("4kGadget", action, username);
	}

	protected void execute(final String url, final Method method,
			final RequestCallback callback) {
		RequestInvokerJsni.execute(url, method.toString(), callback, attributes
				.getApiKey(), attributes.getAuth());
	}

	/**
	 * @return the attributes
	 */
	public HeaderAttributes getAttributes() {
		return attributes;
	}

	/**
	 * @param attributes
	 *            the attributes to set
	 */
	public void setAttributes(final HeaderAttributes attributes) {
		this.attributes = attributes;
	}

	/**
	 * The JavaScript proxy for performing AJAX request within Gadget container.
	 * 
	 * @author Izzet_Mustafayev
	 * 
	 */
	private static class RequestInvokerJsni extends JavaScriptObject {

		/**
		 * The default constructor required by GWT JSNI spec.
		 */
		@SuppressWarnings("unused")
		protected RequestInvokerJsni() {
			super();
		}

		/**
		 * Execute remote AJAX request using <code>gadgets.io.makeRequest</code>
		 * .
		 * 
		 * @param url
		 *            the URL to request to
		 * @param method
		 *            the HTTP request mode
		 * @param callback
		 *            the callback function to call after request completion
		 * @param apiKey
		 *            the remote application API key
		 * @param auth
		 *            the current user auth key to authenticate with remote
		 *            application
		 */
		public final native static void execute(final String url,
				final String method, final RequestCallback callback,
				final String apiKey, final String auth)/*-{
			var params = {}, _headers = {};
			if (method == undefined) {
				method = gadgets.io.MethodType.GET;
			}
			params[gadgets.io.RequestParameters.METHOD] = method;
			params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.TEXT;
			_headers['4KAuth'] = auth;
			_headers['4KApplication'] = apiKey;
			_headers['4KVersion'] = '1.1';
			params[gadgets.io.RequestParameters.HEADERS] = _headers;
			var _callback = function(obj) {
				var domdata = obj.text;
				callback.@name.webdizz.gadget.four.envelope.client.request.RequestCallback::onResponseReceived(Ljava/lang/String;)(domdata);
			};
			if (url.indexOf('?')>0){
				url += '&'+new Date().getTime();
			} else {
				url += '?&'+new Date().getTime();
			}
			gadgets.io.makeRequest(url, _callback, params);
		}-*/;

		public final native static void initTracker()/*-{
			if($wnd._gat == undefined){
				document.write(unescape("%3Cscript src='" + "http://www.google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
			}
		}-*/;

		/**
		 * @param category
		 * @param action
		 * @param label
		 */
		public final native static void trackUsage(final String category,
				final String action, final String label)/*-{
			if($wnd.___4kGadgetTracker == undefined){
				try {
					$wnd.___4kGadgetTracker = $wnd._gat._getTracker("UA-10296791-2");
				} catch(err) {}
			}
			if($wnd.___4kGadgetTracker != undefined){
				try {
					$wnd.___4kGadgetTracker._trackPageview("/4kgadget/"+action+"/"+label);
				} catch(err) {alert(err.message);}
			}
		}-*/;
	}
}
